<?php
/**
 * @property Job $Job
 * @property SessionComponent $Session
 */
class JobsController extends AppController {

	var $name = 'Jobs';
	var $helpers = array('Js', 'MyForm', 'Elfinder');
  
  var $tagDelimiter = ","; // separator to use in the input form (form controller will 'explode' on this).
  var $globalClassFullPath = "D:\My Documents\My Dropbox\School\PILOT\_htdocs\imagerecon\GlobalsClass.php";
  var $flashMessages = array();
  var $flashMsgGlue  = " ";
  // A list of datbase job status id's from the 'enum' table 'statuses'. Useful when changing a JobVersion's
  //  status from 'prepared' to 'submitted', etc.
  var $statusIds     = array('Prepared'=>1, 'Submitted'=>2, 'Processing'=>3, 'Done'=>4);
  
  /**
   * INDEX
   */
	function index() {
    
		$this->Job->recursive = 0;
    //$this->Job->recursive = -1; // just experimenting
		$this->set('jobs', $this->paginate());
	}

  /**
   * VIEW
   * 
   * View a Job and a list of its JobVersions. User then can edit/delete a JobVersion, or add a new version
   * like one from the list of JobVersions.
   */
	function view($id = null) {
    
		if (!$id) {
			$this->Session->setFlash(__('Invalid job id provided to JobsController::view()', true));
			$this->redirect(array('action' => 'index'));
		}
    
    $jobVersions = $this->Job->JobVersion->find('all', array('conditions'=>array('JobVersion.job_id'=>$id)));
    
    $this->set('jobVersions', $jobVersions);
		$this->set('job', $this->Job->read(null, $id));
    
    //debug($id);
    //debug($jobVersions);
	} /////////// end view() //////////////////////////////////////

  /**
   * ADD - Note now using this controller's 'form()' to handle adding/editing Jobs/JobVersions 
   *        only keeping this here for reference of a nearly-original implmentation.
   */
	function add() {
    die("JobsController::add() not being used now.  Tell the developer to update the link to use form() instead.");
    // <editor-fold defaultstate="collapsed" desc="legacy code for ADD (not used now)">
//    ////////////////////////////////////////
//    /////// When saving new data ///////////
//    if (!empty($this->data)) {
//      //debug($this->data);
//      
//      // If no Alt Name provided, don't save to it's table
//      if (empty($this->data['JobAltName']['name'])) {
//        //debug("Looks like this->data['JobAltName']['name'] is EMPTY!");
//        unset($this->data['JobAltName']); // now saveAll(this->data) won't try saving in job_alt_names table
//      }
//      // If no Description provided, don't save to it's table
//      if (empty($this->data['Description']['content'])) {
//        //debug("Looks like this->data['Description']['content'] is EMPTY!");
//        unset($this->data['Description']); // now saveAll(this->data) won't try saving in descriptions table
//      }
//
//      $this->Job->create();
//
//      if ($this->Job->saveAll($this->data, array('validate' => 'first'))) {
//        $this->Session->setFlash(__('The job has been saved', true));
//        $this->redirect(array('action' => 'index'));
//      } 
//      else {
//        $this->Session->setFlash(__('The job could not be saved. Please, try again.', true));
//      }
//    }
//    ////////////////////////////////////////////////
//    ////////// When accessing new add page /////////
//    
//		//$jobTypes = $this->Job->JobType->find('list');
//
//    $tags = $this->Job->Tag->find('list');
//
//    //$this->set(compact('jobTypes'));  // </editor-fold>
  	} //////////////////  end add() ///////////////////////////////////

  /**
   * EDIT - Note now using this controller's 'form()' to handle adding/editing Jobs/JobVersions 
   *        only keeping this here for reference of a nearly-original implmentation.
   */
	function edit($id = null) {
    die("JobsController::edit() not being used now.  Tell the developer to update the link to use form() instead.");
    
  // <editor-fold defaultstate="collapsed" desc="legacy code for EDIT (not using now)">
    
//    // If Invalid (no id provided and no data sent)
//    if (!$id && empty($this->data)) {
//      $this->Session->setFlash(__('Invalid job', true));
//      $this->redirect(array('action' => 'index'));
//    }
//
//
//    // If ready to save data
//    if (!empty($this->data)) {
//      // <editor-fold defaultstate="collapsed" desc="Handle updating/deleting Job Alt Name">
//      // HANDLE ALT NAME
//      // If no Alt Name provided AND the edited record originally HAD an Alt Name
//      // to begin with, then DELETE the old Alt Name from it's table 
//      // and prevent saving this blank replacement.
//      if (empty($this->data['JobAltName']['name']) &&
//
//              is_numeric($this->data['JobAltName']['id'])) { // a numeric id field included if the record had an id to begin with
//        
//        // Delete the Alt Name
//        if ($this->Job->JobAltName->delete($this->data['JobAltName']['id'])) {
//          $this->Session->setFlash(__('Job alt name deleted', true));
//        }         else {
//          $this->Session->setFlash(__('Job alt name was not deleted', true));
//        }
//        unset($this->data['JobAltName']); // now saveAll(this->data) won't try saving in job_alt_names table
//      }
//      // If no Alt Name provided AND the edited record didn't have an Alt Name to begin with,
//      //  then don't save to it's table (and don't try to delete).
//      else if (empty($this->data['JobAltName']['name'])) {
//        //debug("Looks like this->data['JobAltName']['name'] is EMPTY!");
//        unset($this->data['JobAltName']); // now saveAll(this->data) won't try saving in job_alt_names table
//      } // </editor-fold>
//      
//        // <editor-fold defaultstate="collapsed" desc="Handle updating/deleting Description">
//      // HANDLE DESCRIPTIONS (same issues as with Alt Name above)
//      if (empty($this->data['Description']['content']) &&
//              is_numeric($this->data['Description']['id'])) {
//        // Delete the Description
//        if ($this->Job->Description->delete($this->data['Description']['id'])) {
//          $this->Session->setFlash(__('Descripotion deleted', true));
//        } else {
//          $this->Session->setFlash(__('Descripotion was not deleted', true));
//        }
//
//        unset($this->data['Description']); // now saveAll(this->data) won't try saving in descriptions table
//      }
//      // If no Description provided and there never was one, don't save to it's table
//      else if (empty($this->data['Description']['content'])) {
//        unset($this->data['Description']); // now saveAll(this->data) won't try saving in descriptions table
//      }// </editor-fold>
//      
//      
//      // Save All
//      if ($this->Job->saveAll($this->data, array('validate' => 'first'))) {
//        $this->Session->setFlash(__('The job has been saved', true));
//        $this->redirect(array('action' => 'index'));
//      } else {
//        $this->Session->setFlash(__('The job could not be saved. Please, try again.', true));
//      }
//    }
//    ///////////////////////////////////////////////////////////////
//    ////// When accessing new edit page ///////////////////////////
//    if (empty($this->data)) {
//      $this->data = $this->Job->read(null, $id);
//    }
//
//    // these queries not working right... deal with it later/elsewhere.
//    //$tag_ids = $this->Job->JobsTag->find('list', array('condiditions'=>array('JobsTag.job_id'=>$id),
//    //                                                   'fields'      =>array('JobsTag.tag_id'     )));
//    //$tags = $this->Job->JobsTag->Tag->find('list', array('conditions'=>array('Tag.id'=>$tag_ids)));
//
//    $this->set(compact('jobTypes', 'tags')); 
    
    // </editor-fold>
 } //////////////// end edit() ////////////////////////////////

  /**
   * FORM()   Acts as the controller action for both Adding a new Job,
   *          as well as adding a new Job Version to an existing Job.  It will perhaps also be
   *          the controller action for editing existing Jobs or JobVersions.
   * 
   * @param $job_id                Possible values: "new" (for brand new Job), or an existing
   *                                                     Job's id.
   * @param $template_version_id   The id of the JobVersion to use as a template for the parameter
   *                               values for a new JobVersion. 
   *                               Possible values: "new" (just use default vals), or an existing
   *                               JobVersion's id.
   * @param $edit_job_id           If provided, then the user wants to edit an existing Job.
   *                               Note that this option only lets the user edit the Job fields, and not
   *                               any JobVersion fields. If this parameter if passed, $edit_version_id will
   *                               be ignored.
   * @param $edit_version_id       If provided, then the user wants to edit an existing JobVersion,
   *                               and that version will be used to populate form fields 
   *                               ($template_version_id would be ignored).
   *                               Note that if the JobVersion has already been processed, we are probably
   *                               only going to the let the user edit certain JobVersion metadata fields,
   *                               and not any processing parameters. This is because once a job has been 
   *                               processed, it doesn't make sense to be able to change the parameters that
   *                               were used - you can't re-write history!
   */
  function form($job_id = 'new', $template_version_id = 'new', $edit_job_id = "false", $edit_version_id = "false" ) {
     
        //debug statements to show values of params
        //debug($job_id);  debug($template_version_id); debug($edit_job_id);  debug($edit_version_id);
        
    // <editor-fold defaultstate="collapsed" desc="Setup State Vars">
    // Start with state assumptions, and change as necessary below
    $addNewJob = true;
    $addNewJobVersion = true;
    $editJob = false;
    $editJobVersion = false;
    $useTemplate = false;
    $jobVersionNum = 1;
    $showFdk = false;
    $showSirt = false;
    $selectSirtType_Psirt = true;  // select Psirt (true) or Sirt (false) in the SIRT Parameters Fieldset
    $applyGaussianFilter  = false; // for the view - determines whether or not to also show gaussian filter
        
    /////////////////////////////////////////
    //////// SETUP STATE ////////////////////
        
    // Add a new Job and JobVersion
    if ($job_id === 'new') {
    }
    // Add new JobVersion for an existing Job - but DON'T USE A PREV VERSION AS A TEMPLATE
    else if ($template_version_id === 'new') {
      $addNewJob = false;
    }
    // Add new JobVersion and USE A PREV VERSION AS A TEMPLATE
    else if ($edit_version_id == "false") {
      $addNewJob = false;
      $useTemplate = true;
    }


        // Edit a previous Job
    if ($edit_job_id != "false") {
      $addNewJob = false;
      $addNewJobVersion = false;
      $editJob = true;
    }
    // Edit a previous JobVersion
    else if ($edit_version_id != "false") {
      $addNewJob = false;
      $addNewJobVersion = false;
      $editJobVersion = true;
    }// </editor-fold>
               
    ////////////////////////////////////////
    /////// When saving new data ///////////
    if (!empty($this->data)) {
      
      //debug('Initial data is:');   debug($this->data);
      
      // Start TRANSACTION for the PRE-VALIDATION FIELD-REMOVAL PROCESS if in edit mode
      //  (we need to be able to rollback optional-field deletions if the form did not validate).
      //  This is a separate transaction from the one used when saving all data.
      if($editJob || $editJobVersion) {
        $dataSource = $this->Job->getDataSource();
        $dataSource->begin($this->Job);
      }
      //////////////////////////////////////////////////////////////////////////////////////////////////////////
      ///// Setup things before doing validating or saving, such as removing blank Model fields from $data ////////             
      if($addNewJob || $editJob) {
        $this->Job->removeBlankOptionalFields($this->data, $editJob);
      }
      if($addNewJobVersion || $editJobVersion) {
        // if user did not select 'apply gaussian filter', then make Gaussian Sigma field not required
        if( ! $this->data['JobVersion'][0]['param_proj_apply_gaussianfilter'] ) {
          $this->Job->JobVersion->optionalAssocModelsAndFields['ParamProjGaussSigma'] = 'gauss_sigma';
        }
        $this->Job->JobVersion->removeBlankOptionalFields($this->data, $editJobVersion);
        $this->removeUnusedJobTypes($this->data);
        if($editJobVersion) {
          $this->Job->JobVersion->handleDeletingJobTypeRecords($this->data); // remove FDK, PSIRT, SIRT stuff if needed
        }
      } 
      
          //debug('Before trying any saves OR validation (but after removing blank optional fields), this->data is:');
          //debug($this->data);
      ////////////////////////////////////
      ///// Validate & Save All Data /////
      // <editor-fold defaultstate="collapsed" desc="Validate & Save All Data">
      
      // VALIDATE everything first...     
      $validated = true;    
      // Validate these exact Models & their directly associated models - this order required to show all errors at once
      $jobVersionValidated = $this->Job->JobVersion->saveAll($this->data, array('validate' => 'only'));
      $paramFdkValidated =   $this->Job->JobVersion->ParamFdk->saveAll($this->data, array('validate' => 'only'));
      $paramSirtValidated =  $this->Job->JobVersion->ParamSirt->saveAll($this->data, array('validate' => 'only'));
      $jobValidated = $this->Job->saveAll($this->data, array('validate' => 'only'));
      
      if ( ! ($jobValidated && $jobVersionValidated && $paramFdkValidated && $paramSirtValidated) ) {
        $this->flashMessages[] = 'The job did NOT validate! Please correct the marked fields.';
        $validated = false;
      } 
      // <editor-fold defaultstate="collapsed" desc="Earlier Validation scheme (wouldn't show all errors @ same time)">
        //      $validated = true;
        //      if ($this->Job->saveAll($this->data, array('validate' => 'only'))) {
        //
        //        if ($this->Job->validateAllAssociated($this->data,                                                 true, // saveRecursive
        //                        true  // printError  // TODO: turn this to false when not debugging
        //        )) {
        //                  } 
        //          else {
        //          $validated = false;
        //        }
        //      } 
        //        else {
        //        $validated = false;
        //      }
        //
        //      if (!$validated) {
        //        $this->Session->setFlash(__('The job didn\'t validate! Please, correct the marked fields.', true));
        //      }// </editor-fold>
      
      
      // Commit or Rollback the pre-validation field-removal Transaction.
      //  Then continue to either save the fields or return erroneous form to user (bottom part of this method).
      if($editJob || $editJobVersion) {
        if ($validated) {  $dataSource->commit($this->Job);   } 
        else {             $dataSource->rollback($this->Job); }
      }
  
      
      // -------------------------------------------------------------------------
      // Use a TRANSACTION to SAVE all models
      // <editor-fold defaultstate="collapsed" desc="Save all models">
      if ($validated) {
        // Start transaction
        $dataSource = $this->Job->getDataSource();
        $dataSource->begin($this->Job);
        $allSavesSuccessful = true;
        // Save Job first
        $this->Job->create(); // initializes a new Job Model for inserting into DB
        //debug('Before trying any saves (but after removing blank optional fields), this->data is:');
        //debug($this->data);
        $saved = $this->Job->save($this->data, array('validate' => 'first'));
        // --------------------------------------
        // Save all Associated Models recursively
        if ($saved) {
          $success = $this->Job->saveAllAssociated($this->data);
          if (!$success) {
            $allSavesSuccessful = false;
          }
        } 
        else {
          $allSavesSuccessful = false;
        }
        // ----------------------------------
        ////// SAVE UNRELATED MODELS ////////
        // Handle saving TAGS (a 'hasAndBelongTo' relationship - emulated by using a 'join' table)
        if (isset($this->data['Tag']) && isset($this->data['Tag']['name'])) {

          $printDebugPr = false; // do I want func to print it's Pr() statements upon its errors
          $success = $this->saveJobDelimitedTags($this->data['Tag']['name'], $this->tagDelimiter, $printDebugPr);
          if (!$success) {
            $allSavesSuccessful = false;
            $this->flashMessages[] ="Something went wrong saving a Tag (either to the Tag table or to the 'join' table.";
          }
        }
        // Handle saving JOB TYPE params (FDK, etc)
        $success = $this->saveJobTypeParams($this->data); // if data[type base model] is set, it saves to the base table and the 'connector' table
        if (!$success) {
          $allSavesSuccessful = false;
        }
        // --------------------------------------------------------------------------------------------------------------
        // Commit or Rollback the Transaction.  Also initiate Cluster job if checkbox says to. Change status to submitted.
        if ($allSavesSuccessful) {
          $dataSource->commit($this->Job);
          $this->flashMessages[] = 'Job saved to database.';
          // -----------------
          // Submit to Cluster
          if (isset($this->data['submitToCluster']) && $this->data['submitToCluster']) {
            // Generate & save Config.txt
            $configFileSaved = $this->generateConfigFile($this->data);
            if($configFileSaved) {
              $this->flashMessages[] = 'Config file was generated & saved.';
              // Submit to cluster
              $cudaJobNum = $this->submitToCluster($this->Job->JobVersion->id);
              // -----------------------------------------
              // If job successfully submitted to Cudahead
              if( $cudaJobNum) {
                $this->flashMessages[] = 'Job Version submitted to cudahead.';            
                // prepare an array to save to the Cuda table, then SAVE it.
                $cudaJobArray = array('CudaJob'=> array('job_version_id'=>$this->Job->JobVersion->id,
                                                        'cuda_job_num'  =>$cudaJobNum));
                $cudaNumSaveSucceeded = $this->Job->JobVersion->CudaJob->save($cudaJobArray,array('validate'=>'first'));
                if($cudaNumSaveSucceeded) { 
                  $this->flashMessages[] = "Cuda job number $cudaJobNum also stored.";
                }
                else {
                  $this->flashMessages[] = 'Cuda job number could NOT be stored in database.';
                }
                // --------------------------------------
                // Change Version's status to 'submitted'
                $dataArray = array('JobVersion' => array('status_id'=>$this->statusIds['Submitted'] ));
                $saveOptions = array('validate'=>'first', 'fieldList'=>array('status_id'));
                // Assuming the JobVersion's id has been set correctly (by the above save in this case)
                $statusSaved = $this->Job->JobVersion->save($dataArray, $saveOptions);
                if( !$statusSaved ) {
                  $this->flashMessages[] = 'Job Version\'s status could NOT be changed to \'submitted\'.';
                }
              }
              // -----------------------------------------
              // If job could NOT be submitted to cudahead
              else {
                $this->flashMessages[] = 'Job Version could NOT be submitted to the Cudahead...Cudahead may be down.';
              }
            } // end if config file was successfully saved to disk
            // -----------------------------------------
            // If Config file could NOT be saved to disk
            else {
              $jobVersionId = $this->Job->JobVersion->id;
              $this->flashMessages[] = 'Config file could NOT be saved to disk! ' .
                                       "Tried to save it to: {$this->getConfigFileFullPath($jobVersionId)}";
            }
          } ///// end if we should Submit the Job to the Cuda cluster ////
          
          // Redirect to Job view screen that shows a list of a job's versions, etc.
          $this->setMyFlashMsgs();
          $this->redirect(array('action' => 'view', $this->Job->id));
        } 
        else { // all saves were NOT successful
          $dataSource->rollback($this->Job);
          // TODO: figure out if I should blank out previous entries in the flashMessages array when this case occurs.
          $this->flashMessages[] = 'The entire job did NOT save!  There could be a database issue. Please try again.';
          $this->setMyFlashMsgs();
        }
      } // end saving all models// </editor-fold>
      // </editor-fold>


      // <editor-fold defaultstate="collapsed" desc="Old code that did my home-grown 'saveAll()'">
      /*
      /////////////////////////
      ///// Save All Data ///// // <editor-fold defaultstate="collapsed" desc="Old saving data sections">
      $this->Job->create(); // initializes a new Job Model for inserting into DB

      debug('Before trying any saves (but after removing blank optional fields), this->data is:');
      debug($this->data);
      
      $retval = $this->Job->saveAll($this->data, array('validate'=>'first')); 
      if ($retval) {
        pr("Job->saveAll() suppossedly worked.  Returned: $retval");
        $this->Session->setFlash(__('The job has been saved', true));
        
        // Handle saving Tags (a 'hasAndBelongTo' relationship - emulated by using a 'join' table)
        $printDebugPr = false; // do I want func to print it's Pr() statements upon its errors
        $success = $this->saveJobDelimitedTags($this->data['Tag']['name'], $this->tagDelimiter, $printDebugPr);
        if (!$success) {
          //debug("Something went wrong saving a Tag (either to the Tag table or to the 'join' table");
          $this->Session->setFlash("Something went wrong saving a Tag (either to the Tag table or to the 'join' table.");
        }
        //$this->redirect(array('action' => 'index')); // TODO: uncomment this line
      }
      else {
        $this->Session->setFlash(__('The job didn\'t validate!  Please, try again.', true));
      }
  
      // NOTE: Job->saveAll() is failing to insert 'job_id' into the JobVersion table, thus the transaction fails.
      // WORKAROUND: (1) Validate all data first using Job->saveAll (note param 'validate'=>'only'), 
      //             (2) Save the Job using Job->save().
      //             (3) Then use the Job's table row id to set related tables' job_id.
      //             (4) After that, do a saveAll() on each related table that has a 'job_id'. 
      
      // Validate all the data first (note 'validate'=>'only')
      if ($this->Job->saveAll($this->data, array('validate' => 'only'))) {

        // Save the Job data first so we can get its id...
        $retval = $this->Job->save($this->data, array('validate' => 'first'));
        if ($retval) {
          //debug("Supposedly the Job->save() worked.");  debug("retval was:");  debug($retval);

          unset($this->data['Job']); // Remove Job before doing a related Model's saveAll() so its not a dup 
          // Handle saving Tags (a 'hasAndBelongTo' relationship - emulated by using a 'join' table)
          $printDebugPr = false; // do I want func to print it's Pr() statements upon its errors
          $success = $this->saveJobDelimitedTags($this->data['Tag']['name'], $this->tagDelimiter, $printDebugPr);
          if (!$success) {
            debug("Something went wrong saving a Tag (either to the Tag table or to the 'join' table");
          }
          ///// SAVE RELATED TABLES (uses Job model's hasOne & hasMany associations) /////////
          ///// Sets related tables' foreign key 'job_id', then use the related Model's saveAll() 
          ///// Note that JobsTag was saved in saveJobDelimetedTags(), and won't be saved in this func
          /////  since there is no $data['JobsTag'] set.
          if ($this->Job->saveAllAssociated($this->data)) {
            $this->Session->setFlash(__('The job has been saved', true));
          } 
          else {
            debug('Job->saveAllAssociated() failed.');
            $this->Session->setFlash(__('Related models could not save. Please, try again.', true));
          }
          //$this->redirect(array('action' => 'index'));
        } // end if the Job saved
      }
      // If Job validate-only failed
      else {
        $this->Session->setFlash(__('The job didn\'t validate! Please, try again.', true));
      } */ // </editor-fold>
          
      
      
      
    } ///////// end  if(!empty($this->data)) /////////////////// </editor-fold>
        
    ///////////////////////////////////////////////////////////////////////////////////////////////
    ////// PREPARE FOR THE VIEW [When accessing new Add/Edit form (or form didn't validate)] //////
		
    // Read any necessary data for the view
    
    // <editor-fold defaultstate="collapsed" desc="Get data for view">
    
    // --------------------------------------------
    // Cases where we need to pull in some Job data (or prepopulate any fields)
    if ($addNewJob) {
      // Prepulate couple fields
      $this->data['JobVersion'][0]['version_num'] = 1; // The only time we start with version num = 1
      $this->data['JobVersion'][0]['status_id'] = $this->Job->JobVersion->Status->defaultId; 
    }    
    else if($editJob) {
      // only get 'data' if brand new form (don't overwrite 'data' if form didn't validate - keep user's entries for them)
      if(empty($this->data)) {
        $this->Job->contain('Description', 'JobAltName', 'JobsTag', 'JobsTag.Tag');
        $this->data = $this->Job->find('first', array('conditions' => array('Job.id' => $edit_job_id)));

        // Get TAGS into convenient format for view (single string of delimeter-separated tags).
        $tags = array();
        if (isset($this->data['JobsTag']) && !empty($this->data['JobsTag'])) {
          foreach ($this->data['JobsTag'] as $JobsTagIndex => $JobsTagArray) {
            $tags[] = $JobsTagArray['Tag']['name'];
          }
          $this->data['Tag']['name'] = implode($this->tagDelimiter . " ", $tags);
        }
      }
    }  
    else if(($addNewJobVersion || $editJobVersion)) {
      // get Job data to populate text in view (but not as input fields)
      $job = $this->Job->read(null, $job_id);
    } // end cases where we need to pull in some Job data.
    // --------------------------------------------
    // Cases where we need to pull in existing JobVersion data:
    if($addNewJobVersion && $useTemplate) {
      // sets this->data['JobVersion'] and other pertinant Model data with db data
      $this->getJobVersionDataFormatted($template_version_id); 
      $this->setDataDefaults();  // fill in other defaults that the previous version left blank, such as other Job Type fields.
    }   
    else if($editJobVersion) {
      $this->getJobVersionDataFormatted($edit_version_id);
      $this->setDataDefaults();  // fill in other defaults that the previous edit left blank, such as other Job Type fields.
    }
    else if($addNewJobVersion && !$useTemplate) {
      $this->setDataDefaults();
      
      if( !$addNewJob) {
        // Get Job data so view will have Job's id in $data array so the save knows not to create new Job
        $this->Job->recursive = -1;
        $jobArray = $this->Job->find('first', array('conditions' =>array('Job.id' => $job_id )));
        $this->data['Job'] = $jobArray['Job'];
      }
    }
    // --------------------------------------------
    // Set some defaults
    if( !$addNewJob && $addNewJobVersion) {
      $this->data['JobVersion'][0]['status_id']   = $this->Job->JobVersion->Status->defaultId;   
      $this->data['JobVersion'][0]['version_num'] = $this->Job->JobVersion->findNextVersionNum($job_id);
    }
    // -------------------------------------------
    // SET VIEW STATE VARS
    // Set Job Type state for the view
    if(isset($this->data['JobVersion'][0]['job_type_id'])) {
      $jobTypeConfigFileVal = $this->getJobTypeConfigFileVal($this->data);  
      switch($jobTypeConfigFileVal) {
        case 'FDK':
          $showFdk  = true;
          break;
        case 'PSIRT':
          $showSirt = true; // show the <fieldset> that PSRIT and SIRT share
          $selectSirtType_Psirt = true;
          break;
        case 'SIRT':
          $showSirt = true;
          $selectSirtType_Psirt = false;
          break;
        case 'RECON_NONE':
          break;
      }
    }
    // Set Gaussian Filter state for the view (if selected, need to show Gaussian Sigma too) 
    if(isset($this->data['JobVersion'][0]['param_proj_apply_gaussianfilter'])) {  
      if($this->data['JobVersion'][0]['param_proj_apply_gaussianfilter']) {
        $applyGaussianFilter = true;
      } 
    }
    
    // </editor-fold>   
    
    // Get 'enum' table vals so the view's dropdowns get populated
    $statuses = $this->Job->JobVersion->Status->find('list');
		$authors  = $this->Job->JobVersion->Author->find('list');
		$jobTypes = $this->Job->JobVersion->JobType->find('list');
    $paramFdkFilters   = $this->Job->JobVersion->ParamFdk->ParamFdkBasis->ParamFdkFilter->find('list');
    $sirtTypes         = $this->Job->JobVersion->ParamSirt->ParamSirtBasis->SirtType->find('list');
    $sirtSubsetModeXes = $this->Job->JobVersion->ParamSirt->ParamSirtBasis->SirtSubsetModeX->find('list');
    $sirtSubsetModeYs  = $this->Job->JobVersion->ParamSirt->ParamSirtBasis->SirtSubsetModeY->find('list');
    $paramImageComproiTypes = $this->Job->JobVersion->ParamImageComproiType->find('list');
    $paramImageTypes        = $this->Job->JobVersion->ParamImageType->find('list');
    
    
    // Send 'enum' table val vars to view
		$this->set(compact('statuses', 'authors', 'jobTypes', 'paramFdkFilters', 'sirtTypes',
                       'sirtSubsetModeXes', 'sirtSubsetModeYs', 'paramImageComproiTypes', 
                       'paramImageTypes'));
    // Send state vars to view
    $this->set(compact(/*state vars:*/ 'addNewJob','addNewJobVersion','editJob','editJobVersion','versionNum', 
                       /*func params:*/'job_id', 'template_version_id', 'edit_job_id', 'edit_version_id',
                       /*job to be edited:*/  'job',
                                              'showFdk', 'showSirt', 'selectSirtType_Psirt',
                                              'applyGaussianFilter'));
    if(count($this->flashMessages)) {          
      $this->setMyFlashMsgs();
    }
  } /////////////////// end form() ///////////////////////
  
  
  /**
   * Note that this function relies on the Job->id referring to the desired job.
   * 
   * @author Jason Frank 8/4/2011
   * @param string $tagStr
   * @param string $tagDelimiter 
   * @param boolean $printDebugPr  Do you want this method to print its Pr() debug msgs upon errors?
   */
  function saveJobDelimitedTags($tagStr, $tagDelimiter, $printDebugPr = false) {

    // Get a list of tags that were associated with the current Job before it was edited
    // We'll use this list to mark off which tags have been retained by the edit,
    // so that we know which ones to delete from the 'join' table.
    $this->Job->JobsTag->contain('Tag');
    $oldJobTagList = $this->Job->JobsTag->find('all', array('conditions'=>
                                                             array('JobsTag.job_id' => $this->Job->id)));
    $oldJobTagIdList = array();
    // flatten out the array so we have just key=>val as: tag_name=>tag_id
    foreach ($oldJobTagList as $index => $JobsTag_Tag_Array) {
      $tagName = $JobsTag_Tag_Array['Tag']['name'];
      $oldJobTagIdList[$tagName] = $JobsTag_Tag_Array['Tag']['id'];
    }
        //debug("oldJobTagIdList:"); debug($oldJobTagIdList);
    // User's tags
    $tags = explode( $tagDelimiter, $tagStr);
    // Foreach user tag
    foreach($tags as $_tag) {
      // clean up the suppossed tag (it could be whitespace)
      $_tag = trim($_tag);

      // only work with valid, non-blank tags (after being cleaned up)
      if ($_tag) { 
                 //pr("--------------------------- tag: $_tag"); //pr($_tag);

        // Try to find existing tag like this one in the Tag table
        $this->Job->JobsTag->Tag->recursive = -1;
        $tag = $this->Job->JobsTag->Tag->find('first', array('conditions'=>array('name'=>$_tag)));

        // If no matching tag found in database table, save this new one.
        if (!$tag) {
          $this->Job->JobsTag->Tag->create(); // must init new model for saving since loop
          // SAVE THE TAG in Tag table
          $tag = $this->Job->JobsTag->Tag->save(array('name'=>$_tag));
          // save the id
          if($tag !== false) {
            $tag['Tag']['id'] = $this->Job->JobsTag->Tag->id;  //pr("Tag saved: $tag"); //pr($tag);
          }
          else if(!$tag) {
            if($printDebugPr) pr("Tag NOT saved: $_tag"); 
            return false;
          }
        }   
        // Prepare for saving a record in the join table
        $findConditions = array('conditions' => array('JobsTag.job_id'=>$this->Job->id));
        $joinTableJobMatches = $this->Job->JobsTag->find('all', $findConditions );
        $findConditions = array('conditions' => array('JobsTag.job_id'=>$this->Job->id,
                                                      'JobsTag.tag_id'=>$tag['Tag']['id']));      
        $joinTableJobAndTagMatches = $this->Job->JobsTag->find('all', $findConditions );

        // SAVE RECORD TO JOIN TABLE, if the job_id/tag_id pair is not in the join table yet
        if(! count($joinTableJobAndTagMatches)) {
          $this->Job->JobsTag->create(); // must init new model for saving or next iter would replace prev iter's saved record
          $retval = $this->Job->JobsTag->save(array('job_id'=>$this->Job->id,
                                                    'tag_id'=>$tag['Tag']['id']));
          if($retval) { //pr("Record in Join Table saved. job_id: {$this->Job->id}, tag_id: {$tag['Tag']['id']}");
          }
          else {  
            if($printDebugPr) debug('Record in JobsTags Join Table NOT successfully saved.');   
            return false;
          }
        }
        // If this tag matches one that this Job had before it was edited, remove it from this list
        // (any leftover tags in this list will then be removed from the 'join' table below)
        if( isset($oldJobTagIdList[$_tag]) ) {
          unset($oldJobTagIdList[$_tag]);
        }
      } ///// end if we have a valid tag ////////////
    } /// end foreach tag ///////////////
    
    // Delete from the 'join' table any tags that the Job originally had, but have been removed by the user
    // Only 'leftover' tags should remain in this list at this point
    foreach ($oldJobTagIdList as $tagName => $tagId) {
      $conditions = array('JobsTag.tag_id' => $tagId, 'JobsTag.job_id' => $this->Job->id);
      // Delete from 'join' table
      if($this->Job->JobsTag->deleteAll($conditions)) {     }
      else {        debug("JobsTag record FAILED to be successfully deleted");      }
    }
    
    return true; // indicate success
  } /////////////// end saveJobDelimitedTags() ///////////////////
  
  
  /**
   * Sets and formats a number of data items in the this->data array so the JobVersion form view has 
   * the data it is expecting.
   * 
   * Sets this->data['JobVersion'][0][*] by pulling existing JobVersion data from the database and 
   * formatting its array properly for the view (i.e., putting the numerical index in there, etc). 
   * Also pulls related data that should appear as a part of a job version, such as param_sirt_bases
   * or param_image_indir, etc.
   * 
   * @param int $version_id 
   */
  function getJobVersionDataFormatted($version_id)		{
    
    // Get JobVersion data and data from directly related models.
    // only get 'data' if brand new form (don't overwrite 'data' if form didn't validate - keep user's entries for them)
    if(empty($this->data)) {    
      // First get JobVersion table data (then we can pull in related data such as JobType)
      $this->data = $this->Job->JobVersion->find('first', array('conditions' => 
                                                              array('JobVersion.id' => $version_id)));
      // Format the data into a numerically indexed array for the view (hasMany form)
      foreach ($this->data as $ModelName => $fields) {
        if($ModelName == 'JobVersion') {
          foreach($fields as $fieldname => $val ) {
            $this->data['JobVersion'][0][$fieldname] = $this->data['JobVersion'][$fieldname];
            unset ($this->data['JobVersion'][$fieldname]);
          }
        }
      }
      
      // Next, get JobType data.  Sets this->data[basisModelName].
      $jobTypeConfigFileVal = $this->getJobTypeConfigFileVal($this->data);  
      switch($jobTypeConfigFileVal) {
        case 'FDK':
          $this->setDataParamBasis($version_id, 'ParamFdk', 'ParamFdkBasis', 'param_fdk_base_id');
          break;
        case 'PSIRT':
          $this->setDataParamBasis($version_id, 'ParamSirt', 'ParamSirtBasis', 'param_sirt_base_id');
          break;
        case 'SIRT':
          $this->setDataParamBasis($version_id, 'ParamSirt', 'ParamSirtBasis', 'param_sirt_base_id');
          break;
        case 'RECON_NONE':
          break;
      }
      //pr("Before removing 'connector' models from data: "); debug($this->data);
      
      // Clean up data array so it doesn't contain 'base' table entries. When that data gets 
      // sent back to the controller, we don't want those Models to have something exist in the data
      // array, so that those Models don't try to save automatically in the recursive save routine.
      // (those 'connector' tables must be handled manually so we can get approp id's for it).
      if(isset($this->data['ParamFdk' ])) unset($this->data['ParamFdk' ]);
      if(isset($this->data['ParamSirt'])) unset($this->data['ParamSirt']);
      
      //debug($this->data);
    }
  } ////////// end getJobVersionDataFormatted() /////////////////
  
  
  /**
   * Helper for getJobVersionDataFormatted().  Sets $this->data[basisModelName].
   * 
   * @param int    $jobVersionId
   * @param string $connectorModelName
   * @param string $basisModelName
   * @param string $connectorTableParamIdName 
   */
  function setDataParamBasis($jobVersionId, $connectorModelName, $basisModelName, $connectorTableParamIdName ) {

    // Get 'connector' record so we can get id for 'basis' record
    $conditions = array("$connectorModelName.job_version_id" => $jobVersionId );
    $paramConnectorArray = $this->Job->JobVersion->$connectorModelName->find('first', 
                                                                          array('conditions'=>$conditions));
    // Now get record from 'basis' table for the view
    $conditions = array("$basisModelName.id" => 
                         $paramConnectorArray[$connectorModelName][$connectorTableParamIdName]);
    
    $paramBasisArray = $this->Job->JobVersion->$connectorModelName->$basisModelName->find('first',
                                                                array('conditions'=>$conditions));
    $this->data[$basisModelName] = $paramBasisArray[$basisModelName];
    
    //////////////////////////////////////////////////////////////////////////////////////////
    /// FOR REFERENCE - here's what this generalized code is based on - example with FDK /////
    
        //   // Get ParamFdk record so we can get id for ParamFdkBasis record
        //  $conditions = array('ParamFdk.job_version_id' => $version_id );
        //  $paramFdkArray = $this->Job->JobVersion->ParamFdk->find('first', array('conitions'=>$conditions));
        //  // Now get record from ParamBasis for the view
        //  $conditions = array('ParamFdkBasis.id' => $paramFdkArray['ParamFdk']['param_fdk_base_id'] );
        //  $paramFdkBasisArray = $this->Job->JobVersion->ParamFdk->ParamFdkBasis->find('first',
        //                                                              array('conditions'=>$conditions));
        //  $this->data['ParamFdkBasis'] = $paramFdkBasisArray['ParamFdkBasis'];
    
  } //////////////// end setDataParamBasis() ///////////////////////
  
  
  
  
  
  /**
   * Remove unused job type parameters from the $data array, such as Job Types that don't apply 
   * (i.e., user selected FDK type, so remove PSIRT, etc).  
   * 
   * This func determines the Job Type by inspecting $data['JobVersion'][0]['job_type_id']
   * 
   * This function expects to find 
   * the model name as the first index, such as $data['ParamFdkBasis'].
   * 
   * @param array $data 
   */
  function removeUnusedJobTypes(&$data) {
    // First figure out the job type.  Relies on knowing the config_file_val literals.    
    $config_file_val = $this->getJobTypeConfigFileVal($data);
    
    switch ($config_file_val) {
      case 'FDK':
        if(isset($data['ParamSirtBasis'])) unset($data['ParamSirtBasis']);
        break;
      case 'PSIRT':
        if(isset($data['ParamFdkBasis'])) unset($data['ParamFdkBasis']);
        break;
      case 'SIRT':
        if(isset($data['ParamFdkBasis'])) unset($data['ParamFdkBasis']);
        break;
      case 'RECON_NONE':
        if(isset($data['ParamFdkBasis'])) unset($data['ParamFdkBasis']);
        if(isset($data['ParamSirtBasis'])) unset($data['ParamSirtBasis']);
        break;
      default:
        break;
    }
  } /////////// end removeUnusedParams() ///////////
  
  
  /**
   *
   * @param array $data
   * @return string 
   */
  function getJobTypeConfigFileVal(&$data) {
  
    $job_type_id = $data['JobVersion'][0]['job_type_id'];    
    $this->Job->JobVersion->JobType->recursive = -1;
    $jobTypeArray = $this->Job->JobVersion->JobType->find('first', array('conditions'=>array('JobType.id'=>$job_type_id)));    
        //debug('find returned: ');   debug($jobTypeArray);
    $config_file_val =  $jobTypeArray['JobType']['config_file_val'];
  
    return $config_file_val;
  } //////////// end getJobTypeConfigFileVal() ////////////////
  
  
  /**
   * Relies on there only being one Job Type in the $data array, such as:
   *   $data['ParamFdkBasis'].  Also relies on being able to get the JobVersion id
   *   like: $this->Job->JobVersion->id (make sure JobVersion has already been saved).
   *  
   * First save to the 'base' table, then save to the 'job version connector' table
   * 
   * @param array $data 
   * @param boolean $printDebugPr  Do you want to print pr() debug msg's upon failed saves?
   * @return boolean  Indicates success of saving to both tables.
   */
  function saveJobTypeParams(&$data, $printDebugPr = false) {
    
    ///////////
    //// FDK ///
    if(isset($data['ParamFdkBasis'])) {
      
       // create
      $this->Job->JobVersion->ParamFdk->ParamFdkBasis->create(); 
      // save
      $saved = $this->Job->JobVersion->ParamFdk->ParamFdkBasis->save($data, array('validate' => 'first'));
      if(!$saved) {
        if($printDebugPr) pr('this->Job->JobVersion->ParamFdk->ParamFdkBasis->save() failed');
        return false;
      }
      // Proceed to save to the 'job version connector' table
      $base_id = $this->Job->JobVersion->ParamFdk->ParamFdkBasis->id;
      $job_version_id  = $this->Job->JobVersion->id;
      $saved = $this->Job->JobVersion->ParamFdk->save(array('job_version_id'=>$job_version_id,
                                                            'param_fdk_base_id'=>$base_id));
      if(!$saved) {
        if($printDebugPr) pr('this->Job->JobVersion->ParamFdk->save() failed');
        return false;
      }
    }
    //////////////////////
    ///// PSIRT & SIRT ///
    else if(isset($data['ParamSirtBasis'])) {
      
      // create
      $this->Job->JobVersion->ParamSirt->ParamSirtBasis->create(); 
      // save
      $saved = $this->Job->JobVersion->ParamSirt->ParamSirtBasis->save($data, array('validate' => 'first'));
      if(!$saved) {
        if($printDebugPr) pr('this->Job->JobVersion->ParamSirt->ParamSirtBasis->save() failed');
        return false;
      }
      // Proceed to save to the 'job version connector' table
      $base_id = $this->Job->JobVersion->ParamSirt->ParamSirtBasis->id;
      $job_version_id  = $this->Job->JobVersion->id;
      $saved = $this->Job->JobVersion->ParamSirt->save(array('job_version_id'=>$job_version_id,
                                                             'param_sirt_base_id'=>$base_id));
      if(!$saved) {
        if($printDebugPr) pr('this->Job->JobVersion->ParamSirt->save() failed');
        return false;
      }   
    }
    
    return true;
  } /////////// end saveJobTypeParams() /////////////
  
  /**
   * Sets $this->data with default vals if a particular field is not already populated.
   */
  function setDataDefaults() {
     
    // Model: JobVersion
    foreach ($this->Job->JobVersion->defaults['JobVersion'] as $field => $value) {
      if(!isset($this->data['JobVersion'][0][$field]))
        $this->data['JobVersion'][0][$field] = $value;
    }
    // Model: ParamSirtBasis
    foreach ($this->Job->JobVersion->defaults['ParamSirtBasis'] as $field => $value) {
      if(!isset($this->data['ParamSirtBasis'][$field]))
        $this->data['ParamSirtBasis'][$field] = $value;
    }
    // Model: ParamFdkBasis
    foreach ($this->Job->JobVersion->defaults['ParamFdkBasis'] as $field => $value) {
      if(!isset($this->data['ParamFdkBasis'][$field]))
        $this->data['ParamFdkBasis'][$field] = $value;
    }
    
    // OPTIONAL Models
 
    // Model: ParamImageRoiZ
    foreach ($this->Job->JobVersion->defaults['ParamImageRoiZ'] as $field => $value) {
      if(!isset($this->data['ParamImageRoiZ'][$field]))      
        $this->data['ParamImageRoiZ'][$field] = $value;
    }
    // Model: ParamProjGaussSigma
    foreach ($this->Job->JobVersion->defaults['ParamProjGaussSigma'] as $field => $value) {
      if(!isset($this->data['ParamProjGaussSigma'][$field]))      
        $this->data['ParamProjGaussSigma'][$field] = $value;
    }
    // Model: ParamMisclBwYmult
    foreach ($this->Job->JobVersion->defaults['ParamMisclBwYmult'] as $field => $value) {
      if(!isset($this->data['ParamMisclBwYmult'][$field]))      
        $this->data['ParamMisclBwYmult'][$field] = $value;
    } 
    // Model: ParamMisclBwZmult
    foreach ($this->Job->JobVersion->defaults['ParamMisclBwZmult'] as $field => $value) {
      if(!isset($this->data['ParamMisclBwZmult'][$field]))      
        $this->data['ParamMisclBwZmult'][$field] = $value;
    } 
    // Model: ParamMisclImgYpads
    foreach ($this->Job->JobVersion->defaults['ParamMisclImgYpad'] as $field => $value) {
      if(!isset($this->data['ParamMisclImgYpad'][$field]))      
        $this->data['ParamMisclImgYpad'][$field] = $value;
    } 
    // Model: ParamImageClip
    foreach ($this->Job->JobVersion->defaults['ParamImageClip'] as $field => $value) {
      if(!isset($this->data['ParamImageClip'][$field]))      
        $this->data['ParamImageClip'][$field] = $value;
    } 
 
  } /////////////// end setDataDefaults() ///////////////////////////
  
  /**
   *
   * @param array $data 
   */
  function generateConfigFile(&$data) {
     
    // Array to hold lines of config.txt file contents
    $config = array();
    //$configFileFullPath = 'config.txt';  // TODO:  figure out what this should be
    //$configFileFullPath = '_Images/Jobs/NewJobs/TestJob1/config.txt';  // TODO:  figure out what this should be
    
    

    $jobName = $this->getJobName($data); 
    
    $jobAltName = $this->getJobAltName($data);
    if(!isset($jobAltName)) $jobAltName = "(not provided)";
    
    $jobVersionName = (  isset($this->data['JobVersionName']) 
                      && isset($this->data['JobVersionName']['name'])) ? 
                             "{$this->data['JobVersionName']['name']}"  : "(not provided)";
    
    $com = '#';  // comment indicator (at start of line in config.txt
    
    $topComments = "$com This config file has been generated by the Gregor web app.\n\n"
                   . "$com  Job Name:            $jobName\n"
                   . "$com  Job Alt Name:        $jobAltName\n"
                   . "$com  Job Version number:  {$this->data['JobVersion'][0]['version_num']}\n"
                   . "$com  Job Version name:    $jobVersionName\n" 
                   . "$com -------------------------------------------------\n";
                   
    $config['topComments'] = $topComments;
    //------------ Job Type (Task Params) -----------
    $config['taskHeader'] = $this->getConfigFileSectionHeader($com, "Task Parameters");

    $jobTypeConfigFileVal = $this->getJobTypeConfigFileVal($data);

    switch($jobTypeConfigFileVal) {
      case 'FDK':
        $filterId = $data['ParamFdkBasis']['param_fdk_filter_id'];
        $conditions = array('ParamFdkFilter.id' => $filterId);
        $foundArray = $this->Job->JobVersion->ParamFdk->ParamFdkBasis->ParamFdkFilter->find('first',
                                             array('conditions'=>$conditions));
        $filter = $foundArray['ParamFdkFilter']['config_file_val'];
        
        $config['FDK'] = "FDK    $filter  {$data['ParamFdkBasis']['alpha']}";
        break;
      case 'PSIRT':
        $config['PSIRT'] = "PSIRT  ". $this->getConfigFileSirtStr($data);
        break;
      case 'SIRT':
        $config['SIRT'] = "SIRT  ". $this->getConfigFileSirtStr($data);
        break;  
      case 'RECON_NONE':
        $config['RECON_NONE'] = 'RECON_NONE';
        break;
    }
    //---------- Image params ----------------
    $config['imageHeader'] = $this->getConfigFileSectionHeader($com, "Image Parameters");
    
    //-- start with the required image params --
    // Nx, Ny, Nz
    $config['IMAGE'] = "IMAGE  {$data['JobVersion'][0]['param_image_nx']}"
                           ."  {$data['JobVersion'][0]['param_image_ny']}"
                           ."  {$data['JobVersion'][0]['param_image_nz']}";
                           
    $config['COMPROI'] = "COMPROI  {$this->getComproiTypeConfigFileVal($data)}"
                               ."  {$data['JobVersion'][0]['param_image_foa_threshold']}"
                               ."  {$data['JobVersion'][0]['param_image_foa_outsidemax']}";   
    // <U16> | <F32> | <FITS> | <PGM>                            
    $config['IMAGE_TYPE'] = $this->getImageTypeConfigFileVal($data);                               
                               
    $config['VOXEL'] = "VOXEL  {$data['JobVersion'][0]['param_image_dxy']}"
                           ."  {$data['JobVersion'][0]['param_image_dxy']}" /* intentional repeat */
                           ."  {$data['JobVersion'][0]['param_image_dz']}";
    // -- optional image params --
    if(isset($data['ParamImageRoiZ'])) {
      $config['ROI_Z'] = "ROI_Z  {$data['ParamImageRoiZ']['roi_z_0']}  {$data['ParamImageRoiZ']['roi_z_1']}"; 
    }                           
    if(isset($data['ParamImageIndir'])) {
      $config['IMG_INDIR'] = "IMG_INDIR  {$data['ParamImageIndir']['indir']}"; 
    }  
    if(isset($data['ParamImageOutdir'])) {
      $config['IMG_OUTDIR'] = "IMG_OUTDIR  {$data['ParamImageOutdir']['outdir']}"; 
    }  
    if(isset($data['ParamImageClip'])) {
      $config['CLIP'] = "CLIP  {$data['ParamImageClip']['clipmin']}  {$data['ParamImageClip']['clipmax']}"; 
    }  
    
    //---------- Projection params ----------------
    $config['projHeader'] = $this->getConfigFileSectionHeader($com, "Projection Parameters");

    $config['PROJ_INDIR']   = "PROJ_INDIR  {$data['JobVersion'][0]['param_proj_indir']}";
    if(isset($data['ParamProjOutdir']['outdir'])){
      $config['PROJ_OUTDIR'] = "PROJ_OUTDIR  {$data['ParamProjOutdir']['outdir']}";
    }
    $config['STEP']          = "STEP  {$data['JobVersion'][0]['param_proj_step']}";
    
    if($data['JobVersion'][0]['param_proj_apply_medianfilter']) {
      $config['MEDIANFILTER'] = "MEDIANFILTER";
    }
    if($data['JobVersion'][0]['param_proj_apply_gaussianfilter']) {
      $config['GAUSSIANFILTER'] = "GAUSSIANFILTER";
      // this value should be set (enforced by the interface), but I'm checking here just so the code
      //  will continue in case its not set
      if(isset($data['ParamProjGaussSigma']['gauss_sigma']))
        $config['GAUSSIANSIGMA']  = "GAUSSIANSIGMA  {$data['ParamProjGaussSigma']['gauss_sigma']}";
    }
    if($data['JobVersion'][0]['param_proj_apply_downsampling']) {
      $config['DOWNSAMPLE'] = "DOWNSAMPLE";
    }
    
    //---------- Miscellaneous params ----------------
    $config['misclHeader'] = $this->getConfigFileSectionHeader($com, "Miscellaneous Parameters");
    
    if($data['JobVersion'][0]['param_miscl_reserror']) {
      $config['RESERROR'] = "RESERROR";
    }
    if(isset($data['ParamMisclBwYmult']['bw_ymult'])) {
      $config['BWYMULT'] = "BWYMULT  {$data['ParamMisclBwYmult']['bw_ymult']}";
    }
    if(isset($data['ParamMisclBwZmult']['bw_zmult'])) {
      $config['BWZMULT'] = "BWZMULT  {$data['ParamMisclBwZmult']['bw_zmult']}";
    }
    if(isset($data['ParamMisclImgYpad']['img_ypad'])) {
      $config['IMGYPAD'] = "IMGYPAD  {$data['ParamMisclImgYpad']['img_ypad']}";
    }
    
    
    //pr("Here's the config array:");  debug($config);
    //die("generateConfigFile() now intentionally dying.");
    
    
    //////////////////////////////////////////////////
    ////// Write config.txt to disk  /////////////////
    $config_str = implode("\n", $config );
    //$bytes_written = file_put_contents($configFileFullPath, $config_str);
    $jobVersionId = $this->Job->JobVersion->id;
    $bytes_written = file_put_contents($this->getConfigFileFullPath($jobVersionId), 
                                       $config_str);
    
    if($bytes_written === FALSE) {
      //$error_str = "\n<p>file_put_contents() failed to write \"config.txt\" to disk.</p>\n";
      //$this->printD($error_str);
      //$this->flashMessages[] = 'Config file could NOT be written to disk!';
      return false;
    }
    else {
      //$this->flashMessages[] = "Config.txt was written to: $configFileFullPath.";
      return true;
    }   
  } //////////// end generateConfigFile() /////////////////////
  
  
  /**
   * Obtain from database and return full path that the config file should be written to.
   */
  function getConfigFileFullPath($jobVersionId) {
    
    $conditions = array('JobVersion.id'=>$jobVersionId);
    $this->Job->JobVersion->contain('Job');
    $foundArray = $this->Job->JobVersion->find('first',array('conditions'=>$conditions)); 

    // TODO: figure out what config.txt will really be called.
    return $foundArray['Job']['dir_abs_path'] . "/config.txt";
  } /////////// end getConfigFileFullPath() //////////////////////
  
  
  /**
   * @param array $data 
   */
  function getImageTypeConfigFileVal(&$data) {
     
    $image_id = $data['JobVersion'][0]['param_image_type_id'];    
    $this->Job->JobVersion->ParamImageType->recursive = -1;
    $conditions = array('ParamImageType.id'=>$image_id);
    $foundArray = $this->Job->JobVersion->ParamImageType->find('first', 
                                                               array('conditions'=> $conditions));    
    return  $foundArray['ParamImageType']['config_file_val'];
  } //////////////////////////////////////////////

   /**
   *
   * @param type $data 
   */
  function getComproiTypeConfigFileVal(&$data) {
    
    $comproi_id = $data['JobVersion'][0]['param_image_comproi_type_id'];    
    $this->Job->JobVersion->ParamImageComproiType->recursive = -1;
    $conditions = array('ParamImageComproiType.id'=>$comproi_id);
    $foundArray = $this->Job->JobVersion->ParamImageComproiType->find('first', 
                                                               array('conditions'=> $conditions));    
    return  $foundArray['ParamImageComproiType']['config_file_val'];
  } /////////////////////////////////////////


   /**
   * @param string $com
   * @param string $str
   * @return string
   */
  function getConfigFileSectionHeader($com, $str) {
    return
     "\n$com -----------------------------------------\n"
      ."$com --------- $str --------\n";
  } /////////////////////////////////////////////////////
  
  
  /**
   * @param type $data 
   * @return string containing all parameters (everything but the "PSRIT" or "SIRT" @ beg of line).
   */
  function getConfigFileSirtStr(&$data) {
    
    // subset mode x
    $xId = $data['ParamSirtBasis']['sirt_subset_mode_x_id'];
    $conditions = array('SirtSubsetModeX.id' => $xId);
    $foundArray = $this->Job->JobVersion->ParamSirt->ParamSirtBasis->SirtSubsetModeX->find('first',
                                         array('conditions'=>$conditions));
    $subset_modeX = $foundArray['SirtSubsetModeX']['config_file_val'];
    // subset mode y
    $yId = $data['ParamSirtBasis']['sirt_subset_mode_y_id'];
    $conditions = array('SirtSubsetModeY.id' => $yId);
    $foundArray = $this->Job->JobVersion->ParamSirt->ParamSirtBasis->SirtSubsetModeY->find('first',
                                         array('conditions'=>$conditions));
    $subset_modeY = $foundArray['SirtSubsetModeY']['config_file_val'];

    return  "{$data['ParamSirtBasis']['alpha']}"
           ."  {$data['ParamSirtBasis']['niter']}"
           ."  {$data['ParamSirtBasis']['nsubs']}"
           ."  $subset_modeX  $subset_modeY"; 
  } //////////// end getConfigFileSirtStr() ////////////
  
  /**
   * Function assumes that either data[Job][name] OR Job->id is set appropriately.
   * 
   * @param array $data
   * @return string: Job's name 
   */
  function getJobName(&$data) {
    
    if(isset($data['Job']['name'])) 
      return $data['Job']['name'];
    // otherwise we need to get job's name from db
    else {      
      $this->Job->recursive = -1;
      $foundArray = $this->Job->find('first', array('conditions'=>array('Job.id'=> $this->Job->id)));
      return $foundArray['Job']['name'];
    }
  } ///////////// end getJobName() ////////
  
   /**
   * Function assumes that either data[JobAltName][name] OR
   *   JobVersion->id is set appropriately.
   * 
   * @param array $data
   * @return either the Job's alternate name if it exists, or null if it does not.
   */
  function getJobAltName(&$data) {
    
    if(isset($data['JobAltName']['name'])) 
      return $data['JobAltName']['name'];
    // otherwise we need to get job's name from db
    else { 
      $this->Job->contain('JobAltName');
      $conditions = array('Job.id'=> $this->Job->id);
      $foundArray = $this->Job->find('first',array('conditions'=>$conditions));
      //pr("foundArray: "); debug($foundArray);
      
      if(isset($foundArray['JobAltName']['name'])) 
        return $foundArray['JobAltName']['name'];
      else
        return null;
    }
  } ///////////// end getJobAltName() ////////
  
  
  /**
   * submitToCluster()
   * 
   * @param int $jobVersionId 
   * @return If the job gets successfully submitted to the Cudahead: 
   *              Cuda Job Number that the Cudahead assigns to this submitted job.  The caller
   *              should use this number to insert into the db as the JobVersion's CudaJob.cuda_job_num.
   *         Else, if the job does NOT get successfully submitted to the Cudahead (perhaps b/c that machine
   *               is down), false will be returned.
   */
  function submitToCluster($jobVersionId) {
  
    $cudaJobNum = false;
    // Get GlobalsClass file.  I guess I have to use full path here.
    require_once "{$this->globalClassFullPath}";
    $privateKeyFullPath = GlobalsClass::getPrivateKeyFullPath();
    
    
    $outputArray = array();        
    //$submitScript = "/home/jfrank3/webhome/imagerecon/_OTHER/Tests/cudaSubmit_helloWorld";
    
    // TODO: ask Gregor what the name of his submit script will be (job name with 'ctrl' appended?)
    $submitScriptName = "cudaSubmit_helloWorld";
    $jobAbsPath = $this->getJobAbsPath($jobVersionId);
    $submitScriptFullPath = "$jobAbsPath/$submitScriptName";
    
    $initCuda = "source /usr/local/modules/init/zsh";
    $loadTorque = "module load torque";
    
    //$cudaUser = $this->getUtCudaUsername($jobVersionId);
    $cudaUser = "jfrank3"; // right now only works for my username for some reason
    
    // TODO: decide if this should be used (or will Gregor's submit script handle that?)
    // cd to this dir so cudahead's stdout/stderr files get placed there
        //$outFileDir = "/home/jfrank3/webhome/imagerecon/_OTHER/Tests/"; 
    // TODO: ? append the Image Output Dir (if it exists)? Gregor does something like that in his ctrl scrip
    $outFileDir = $jobAbsPath; 
    
    $machineToLogInto = GlobalsClass::getCudaMachineToLogInto();

    $cmdsForCudahead = "$initCuda; $loadTorque; showq; cd $outFileDir; qsub $submitScriptFullPath";
    
    $cmdStr = "ssh -i $privateKeyFullPath $cudaUser@$machineToLogInto '$cmdsForCudahead'";
    
        //pr("About to execute: $cmdStr");
    $lastLine = exec($cmdStr, $outputArray, $returnStatus);
        //pr("\nReturn status of the exec() call was: $returnStatus. outputArray is:");
        //foreach($outputArray as $line) {   pr("$line\n");    }
    
    // Grep out the Cuda Job Num
    $matchesArray = array();
    $cudaJobPattern = "/^(\d+)\.cudahead\.eecs\.utk\.edu$/";
    $foundAMatch = 0;
    foreach ($outputArray as $line) {
      $foundAMatch = preg_match($cudaJobPattern, $line, $matchesArray);
      if($foundAMatch) break;
    }
    if($foundAMatch) {
      $cudaJobNum = $matchesArray[1]; // grab first parenthesized match
    }
    else {
      $cudaJobNum = false;
    }
        //pr("matched line: $matchesArray[0]"); pr("matched job num: '$cudaJobNum'");
        //die("submitToCluster() now dying intentionally");
    return $cudaJobNum;
  } /////////////// end submitToCluster() ////////////// 

  /**
   * Helper for submitToCluster(), etc.  Returns absolute path of the Job's directory,
   * by getting the value from the db.  This str should include the job's directory, but not include 
   * a trailing "/".  However, these details are currently up in the air, as we are not sure if we
   * will check the user's entry to confirm it meets these qualifications.
   * 
   * @param int $jobVersionId 
   */
  function getJobAbsPath($jobVersionId) {
    
    $this->Job->JobVersion->contain('Job');
    $conditions = array('JobVersion.id'=>$jobVersionId);
    $jobArray = $this->Job->JobVersion->find('first', array('conditions'=>$conditions));
    
    return $jobArray['Job']['dir_abs_path'];
  } ///////// end getJobAbsPath() ///////////////
  
  /**
   * Helper for submitToCluster(), etc.
   * 
   * @param int $jobVersionId 
   */
  function getUtCudaUsername($jobVersionId) {
    
    $this->Job->JobVersion->contain('Author');
    $conditions = array('JobVersion.id'=>$jobVersionId);
    $authorArray = $this->Job->JobVersion->find('first', array('conditions'=>$conditions));
    return $authorArray['Author']['ut_cuda_username'];
  } ////////////// end getCudaUsername() ////////////
  
  
  /**
   * 
   */
  function setMyFlashMsgs() {
    
    $this->Session->setFlash(join($this->flashMsgGlue, $this->flashMessages));
  } ////// end setMyFlashMsgs() ///////////
  
  /**
   * DELETE
   */
	function delete($id = null) {
    
		if (!$id) {
			$this->Session->setFlash(__('Invalid id for job', true));
			$this->redirect(array('action'=>'index'));
		}
		if ($this->Job->delete($id)) {
      
			$this->Session->setFlash(__('Job deleted', true));
			$this->redirect(array('action'=>'index'));
		}
    
		$this->Session->setFlash(__('Job was not deleted', true));
		$this->redirect(array('action' => 'index'));
	} ////////////////////// end delete() //////////////////
} ///////////////////////////////// end class //////////////////////
